"""
StockBrowser Widget.

This and ui_stockbrowser.py form the StockBrowser widget.
"""

from PyQt4.QtCore import *
from PyQt4.QtGui import *
import ui_widget_stockbrowser
from misc import misc
import guiaux
import system.navalasystem as na

M_STOCKSET = 0
M_ALL = 1

class StockBrowser(QWidget, ui_widget_stockbrowser.Ui_StockBrowser):
    # I am sure there will be some __pyqtSignals__ = ("timeChanged(QTime)", "timeZoneChanged(int)")
    """StockBrowser Widget.
    
    Note that this is not prepared to plug into Qt Designer at the moment."""


    # Emitted whenever the current stock changes in one of the table widgets.
    # This signal is only emitted in single selection mode.
    # The sent information is [[id0, name0, symbol0], [id1, ...], ...]
    CurrentStockChanged = pyqtSignal(list)

    # Properties
    # (=True) Whether the selection should be single or multiple in the table
    # widgets.
    _flagMultiSelection = True
    
    ###########################################################################
    ## SECTION 1: Initialization
    
    def __init__(self, parent=None):
        super(StockBrowser, self).__init__(parent)
        self.logger = misc.GetLogger("stockbrowser")

        ## State variables
        # Filter strings (separated by comma)
        self.sFilter = ""
        # Either M_STOCKSET or M_ALL
        self.iMode = M_STOCKSET
        # Sequence containing result returned by table.read() (PyTables)
        self.aStockset = None
        # Index of selected stockset
        self.iStockset = -1
        # Whether the stocks from corresponding tabs have been already read
        self._flagsRead = [False, False]
        # Current tablewidget row
        self._rows = [-1, -1]
        

        self.setupUi(self)
        self.InitializeUI0()
        self.InitializeUI()
        
    @misc.CatchException
    def InitializeUI0(self):
        """
        This routine sets control properties at runtime (the ones that cannot
        be set from QtDesigner).
        """
        
        
        # Adds "maximize" button
        #self.setWindowFlags(self.windowFlags() | \
        #                    Qt.WindowMaximizeButtonHint | \
        #                    Qt.WindowSystemMenuHint | \
        #                    Qt.WStyle_MinMax)

        tt = self.GetTableWidgets()
        for t in tt:
            t.setAlternatingRowColors(True)
            t.setEditTriggers(QTableWidget.NoEditTriggers)
            t.setSelectionBehavior(QTableWidget.SelectRows)
        self.UpdateMultiSelection()
        
    @misc.CatchException
    def InitializeUI(self):
        self.ReadStocksets()
        self.ReadStocks()
        self.UpdateStockTab()
        self.UpdateComboBoxStockset()
        
        
    ##########################
    ## ?.? Property getters/setters
    
    def SetFlagMultiSelection(self, x):
        self._flagMultiSelection = x
        self.UpdateMultiSelection()
        
    def GetFlagMultiSelection(self):
        return self._flagMultiSelection


    ##########################        
    ## ?.? GUI Writers

    def UpdateMultiSelection(self):
        mode = QTableWidget.MultiSelection if self._flagMultiSelection \
               else QTableWidget.SingleSelection
        for t in self.GetTableWidgets():
            t.setSelectionMode(mode)


    ##########################
    ## 2.2 GUI Readers
    
    def GetTableWidgets(self):
        return [self.tableWidgetStockset, self.tableWidgetAll]
    
    def GetCurrentTableWidget(self):
        if self.iMode == M_STOCKSET:
            return self.tableWidgetStockset
        else:
            return self.tableWidgetAll
    
    def GetCurrentStocks(self):
        """Returns list of tuples (id, name, symbol)
       
        The list represents the currently selected stocks in one of the
        table widgets
        """
        a = []
        t = self.GetCurrentTableWidget()
        aRows = frozenset([item.row() for item in t.selectedIndexes()])
        for iRow in aRows:
            item = t.item(iRow, 0)
            s = str(item.text())
            a.append(item.data(Qt.UserRole).toPyObject())
        return a
        
    def GetCurrentStockNames(self):
        return [x[1] for x in self.GetCurrentStocks()]
        
    def GetCurrentStock(self):
        """Returns tuple (id, name, symbol)
       
        The result represents the table row that has focus.
        """
        a = ()
        t = self.GetCurrentTableWidget()
        iRow = t.currentRow()
        if iRow >= 0:
            item = t.item(iRow, 0)
            s = str(item.text())
            a = (item.data(Qt.UserRole).toPyObject())
        return a
        
    def GetCurrentStockset(self):
        a = ()
        if len(self.aStockset) > 0 and self.iStockset > -1:
            a = self.aStockset[self.iStockset]
        return a
        
    def GetCurrentSS(self):
        """SSF stands for Stockset, Stocks.
        """
        stockset = self.GetCurrentStockset()
        stocks = []
        if len(stockset) > 0:
            stocks = self.GetCurrentStocks()
        return (stockset, stocks)
        
        
    ##########################
    ## 2.3 Database Readers

    def ReadStocksets(self):
        self.aStockset = list(na.vala.db.Read("/stockset"))
        self.aStockset.sort(key=(lambda x: x[1]))
        self.iStockset = 0 if len(self.aStockset) > 0 else -1
    
    def ReadStocks(self):
        """
        If mode is M_STOCKSET, loads stocks from given stockset;
        If mode is M_ALL, loads all stocks.
        
        the list is subsequently passed through the filter
        
        This routine is an exception to not touching the UI: stock data
        is stored directly into the QTableWidget. I have to do this to use
        the automatic sort-by-column feature.
        
        Another exception: filter string is directly retrieved from editFilter
        """
        
        aStock = []
        
        if self.iMode == M_STOCKSET:
            if self.iStockset > -1:
                iId = self.GetCurrentStockset()[0]
                temp = na.vala.db.ReadWhere("/stocksetitem", \
                    "idstockset == %d" % iId)
                for row in temp:
                    # "row[2]"??? See definition of stocksetitem table in
                    # database.py;
                    # I miss SQL's joins
                    temp2 = na.vala.db.ReadWhere("/stock", \
                        "id == %d" % row[2]) 
                    aStock.append(temp2[0])
        elif self.iMode == M_ALL:
            aStock = na.vala.db.Read("/stock")
            
        s = str(self.lineEditFilter.text())
        if len(s) > 0 and not s.isspace():
            temp0 = aStock
            aFilters = map(str.upper, map(str.strip, s.split(',')))
            for sFilter in aFilters:
                temp1 = []                
                for row in temp0:
                    if sFilter in row[4] or sFilter in row[5]: 
                        temp1.append(row)
                temp0 = temp1
            aStock = temp0

        t = self.GetCurrentTableWidget()
        guiaux.PopulateStockTable(t, aStock)
        self._flagsRead[self.iMode] = True

    ###########################################################################
    ## SECTION 3: Database Writers

    def AddSelectedStocks(self, idstockset):
        """Add selected stocks to given stockset."""
        
        ids_stocks = [x[0] for x in self.GetCurrentStocks()]
        na.vala.db.stocksetitem_InsertMany(idstockset, ids_stocks)
        stockset = self.GetCurrentStockset()
        if not misc.isempty(stockset):
            if idstockset == stockset[0]:
                # Forces stockset table widget to be updated
                self._flagsRead[0] = False

    def RemoveSelectedStocks(self):
        """Removes selected stocks from selected stockset."""
        if self.iMode != M_STOCKSET: # will work anyway, just to enforce logic
            raise Exception("RemoveSelectedStocks() shouldn't be called now!")
        
        (stockset, stocks) = self.GetCurrentSS()
        na.vala.db.stocksetitem_DeleteMany(stockset[0],
            [x[0] for x in stocks])


    ###########################################################################
    ## SECTION 4: UI updating routines according to internal states
    
    def UpdateStockTab(self):
        """Changes currentIndex of the Stock Tab"""
        # Trick that is possible because the mode codes M_* match the order
        # of appearance of the tabs
        self.tabWidgetStock.currentIndex = self.iMode
#        self.readStocks()
    
    def UpdateComboBoxStockset(self):
        """Populates the Stockset combo box based on self.aStockset."""
        self.comboBoxStockset.clear()
        for row in self.aStockset:
            self.comboBoxStockset.addItem(row[1])
        if self.iStockset > -1:
            self.comboBoxStockset.setCurrentIndex(self.iStockset)


    ###########################################################################
    ## SECTION 5: Slots
    ## Guidelines for method construction
    ## - Write to database (optional)
    ## - Update internal states
    ## - Call some UI updating routine

    @misc.CatchException
    @pyqtSignature("")
    def on_pushButtonStocksetNew_clicked(self):
        text, ok = QInputDialog.getText(self, 'Create new Stock Set', 
            'New Stock Set name:')
        
        if ok:
            na.vala.db.stockset_Insert(str(text))
            self.ReadStocksets()
            self.UpdateComboBoxStockset()
            index = self.comboBoxStockset.findText(text)
            self.comboBoxStockset.setCurrentIndex(index)
            self.iStockset = index
            
    @misc.CatchException
    @pyqtSignature("int")
    def on_comboBoxStockset_currentIndexChanged(self, index):
        self.iStockset = index
        self.ReadStocks()

    @misc.CatchException
    @pyqtSignature("int")
    def on_tabWidgetStock_currentChanged(self, index):
        self.iMode = index
        if not self._flagsRead[index]:
            self.ReadStocks()


#####    @misc.CatchException
#####    @pyqtSignature("int, int, int, int")
#####    def on_tableWidgetStockset_currentCellChanged(self, row, col, row0, col0):
#####        self.CurrentTableCellChanged()
#####        
#####    @misc.CatchException
#####    @pyqtSignature("int, int, int, int")
#####    def on_tableWidgetAll_currentCellChanged(self, row, col, row0, col0):
#####        self.CurrentTableCellChanged()
        
    @misc.CatchException
    @pyqtSignature("")
    def on_tableWidgetStockset_itemSelectionChanged(self):
        self.CurrentTableCellChanged() 
        
    @misc.CatchException
    @pyqtSignature("")
    def on_tableWidgetAll_itemSelectionChanged(self):
        self.CurrentTableCellChanged()   
      
    def CurrentTableCellChanged(self):
        self.CurrentStockChanged.emit(self.GetCurrentStocks())

            
###################    @misc.CatchException
###################    @pyqtSignature("")
###################    def on_pushButtonFilter_clicked(self):
###################        self.ReadStocks()

    @misc.CatchException
    @pyqtSignature("")
    def on_pushButtonRefresh_clicked(self):
        self.ReadStocks()

    @misc.CatchException
    @pyqtSignature("QPoint")
    def on_tableWidgetStockset_customContextMenuRequested(self, position):
        """
        Mounts and shows popupmenu for the tableWidget control.
        
        When stocks are being filtered, adds an option to add selected stock
        to stockset. Otherwise adds an option to remove it.
        
        """
        self.TableContextMenu(position, self.tableWidgetStockset)
    
    
    @misc.CatchException
    @pyqtSignature("QPoint")
    def on_tableWidgetAll_customContextMenuRequested(self, position):
        self.TableContextMenu(position, self.tableWidgetAll)

    
    def TableContextMenu(self, position, tableWidget):

        menu = QMenu()
        
        (stockset, stocks) = self.GetCurrentSS()
        aActions = []
        aFlagAdd = [] # Whether the action is to add or remove
        if self.iMode == M_ALL:
            if len(stocks) > 0:
                for ss in self.aStockset:
                    aActions.append(menu.addAction("Add to stockset \"%s\"" % \
                                                   ss[1]))
                    aFlagAdd.append(True)

###            if not misc.isempty(stockset) and len(stocks) > 0:
###                aAdd = menu.addAction("Add to stockset \"%s\"" % stockset[1])
###                if np.all(flags): # All stocks already included in stockset
###                    menu.actions()[0].setEnabled(False)
        elif self.iMode == M_STOCKSET:
            if len(stocks) > 0:
                ss_curr = self.GetCurrentStockset()[0] # id of current stockset
                f = lambda x: "Add to" if x else "Remove from"
                for ss in self.aStockset:
                    flagAdd = ss[0] != ss_curr
                    aActions.append(menu.addAction("%s stockset \"%s\"" % \
                                                   (f(flagAdd), ss[1])))
                    aFlagAdd.append(flagAdd)
                    
###                aRemove = menu.addAction("Remove from stock set")
        
        action = menu.exec_(tableWidget.mapToGlobal(position))

        if action in aActions: # otherwise the user didn't choose
            idx = aActions.index(action)
            if aFlagAdd[idx]:
                self.AddSelectedStocks(self.aStockset[idx][0])
                # Does not need GUI update because the change to database does
                # not reflect in anything visible
            else:
                # Only called if in Stockset tab; ReadStocks() will take care
                # of the table widget update
                self.RemoveSelectedStocks()
                self.ReadStocks()
        
        
    @pyqtSignature("QString")
    def on_lineEditFilter_textChanged(self, newText):
        """Resets _flagsRead so that the tables are automatically refreshed
        when the user changed the tab."""
        self._flagsRead = [False, False]
